package com.csap.framework.oauth2.security;

import cn.dev33.satoken.oauth2.logic.type.SecurityStrategy;
import com.csap.framework.oauth2.security.filter.FilterProperties;
import com.csap.framework.oauth2.security.type.SecurityCondition;
import com.csap.framework.util.CollectionsUtil;
import com.csap.framework.util.optional.Optional;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.http.HttpMethod;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @author yangchengfu
 * @dataTime 2021年-07月-27日 14:07:00
 **/
@Data
@ConfigurationProperties(prefix = Oauth2SecurityProperties.PREFIX)
public class Oauth2SecurityProperties {
    public static final String PREFIX = "csap.oauth2.security";
    private String dataId;
    private String group = "DEFAULT_GROUP";
    /**
     * 是否开启
     */
    private Boolean enabled = Boolean.FALSE;

    /**
     * 全局自定义的头部信息
     */
    private Map<String, String> headers = Collections.emptyMap();
    /**
     * 过滤授权的路径集合
     * key1 is http method(get post put delete)
     * key->value{mapping name,mapping url list}
     */
    private Map<String, Map<HttpMethod, Set<String>>> mapping = Collections.emptyMap();
    /**
     * 授权请求
     * key is {SecurityStrategy}
     */
    private Map<SecurityStrategy, List<SecurityStrategyProperties>> authorizeRequests = Collections.emptyMap();

    /**
     * 获取策略规则匹配
     *
     * @param securityStrategy 策略
     * @return 当前策略的权限规则
     */
    public List<FilterProperties> getSecurityStrategy(SecurityStrategy securityStrategy) {
        return authorizeRequests.getOrDefault(securityStrategy, Collections.emptyList())
                .stream()
                .map(i -> FilterProperties
                        .builder()
                        .mapping(toMap(getMappingMethodUrl(filterMapping(i.getMappingName()))))
                        .exclude(toMap(getMappingMethodUrl(filterMapping(i.getExcludeMappingName()))))
                        .condition(i.getCondition())
                        .permissions(i.getPermissions())
                        .build())
                .collect(Collectors.toList());
    }

    /**
     * 获取实际路径
     *
     * @param securityStrategy 策略
     * @return 返回路径
     */
    public List<Map<HttpMethod, Set<String>>> getMappingPath(SecurityStrategy securityStrategy) {
        if (CollectionsUtil.isNotEmpty(authorizeRequests) && authorizeRequests.containsKey(securityStrategy)) {
            return getMappingMethodUrl(filterMapping(authorizeRequests
                    .get(securityStrategy)
                    .stream()
                    .map(SecurityStrategyProperties::getMappingName)
                    .flatMap(Collection::stream)
                    .collect(Collectors.toSet())));
        }
        return Collections.emptyList();
    }

    /**
     * 转换成map
     *
     * @param list
     * @return
     */
    public Map<HttpMethod, Set<String>> toMap(List<Map<HttpMethod, Set<String>>> list) {
        return Optional.of(new ConcurrentHashMap<HttpMethod, Set<String>>(16))
                .when(i3 -> list.stream()
                        .flatMap(i -> i.entrySet().stream())
                        .forEach(i -> i3.computeIfAbsent(i.getKey(), i2 -> Sets.newHashSet()).addAll(i.getValue())))
                .get();
    }

    /**
     * 获取实际的路径
     *
     * @param mapping map路径map
     * @return 实际路径list
     */
    public List<Map<HttpMethod, Set<String>>> getMappingMethodUrl(Map<String, Map<HttpMethod, Set<String>>> mapping) {
        return Lists.newArrayList(mapping.values());
    }

    /**
     * 过滤返回具体策略
     *
     * @param mappingName 映射名称
     * @return 返回具体映射路径
     */
    public Map<String, Map<HttpMethod, Set<String>>> filterMapping(Set<String> mappingName) {
        return mapping.entrySet()
                .stream()
                .filter(i -> mappingName.contains(i.getKey()))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
    }

    @Data
    public static class SecurityStrategyProperties {
        /**
         * 映射URL策略名称
         */
        private Set<String> mappingName = Collections.emptySet();

        /**
         * 过滤的映射URL策略名称
         */
        private Set<String> excludeMappingName = Collections.emptySet();
        /**
         * 条件
         */
        private SecurityCondition condition = SecurityCondition.AND;
        /**
         * 权限列表
         */
        private Set<String> permissions = Collections.emptySet();
    }
}
